package suzuki;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Iterator;
import java.util.Set;

import myLogger.MyLogger;

import Launcher.*;
import portes.Algo;
import portes.Message;
import portes.PorteImpl;


public class JetonSuzukiKasami extends UnicastRemoteObject implements Algo, Client {
	/**
	 * 
	 */
	private static final long serialVersionUID = 8223394217301362731L;
	private int H;
	private int nbrPlaces;
	private boolean AJ, SC;
	private int FH[];
	private Reso reso;
	private MyLogger log;
	private PorteImpl porte;
	private Set<Integer> allClients;
	private MSGJETON jeton;

	public int[] getFH() {
		return FH;
	}

	private boolean premiereFoi;

	public JetonSuzukiKasami(String serveur, int port, int nbrPlaces, PorteImpl porteImpl) throws RemoteException{
		super();
		H=0;
		this.nbrPlaces = nbrPlaces;
		AJ = false;
		SC = false;
		porte = porteImpl;
		premiereFoi = true;

		log = new MyLogger("Suzuki_kasami_Jeton");
		try {
			reso = (Reso) Naming.lookup("rmi://" + serveur + ":" + port + "/" + Reso.NAME);
		} catch (MalformedURLException e ) {
			log.writeln("Erreur connection objet Reso...\n"+e);
		} catch ( NotBoundException e ) {
			log.writeln("Erreur connection objet Reso...\n"+e);
		}
		log.writeln("Connection avec RESO etablie...");

		try {
			log.write("Declaration de la porte a RESO...");
			porte.setIdAttribue(reso.declareClient(this));
			log.writeln("Porte declarer. Numero attribue : "+porte.getIdAttribue());
		} catch (RemoteException e) {
			log.writeln("Erreur declaration porte "+e);
		}

		initialisation();
	}

	public void initialisation(){
		if(porte.getIdAttribue() == 0){
			AJ = true;
		}
	}

	public void initFH(){
		int nbrPortes;
		try {
			allClients = reso.getClients();
			nbrPortes = allClients.size();
			FH = new int[nbrPortes];
			for(int i = 0;i<nbrPortes;i++){
				FH[i] = 0;
			}
			
			jeton = new MSGJETON(nbrPlaces, allClients.size());
			
		} catch (RemoteException e) {
			System.out.println();e.printStackTrace();
		}
	}

	public synchronized void acceptationDemandeSC(int typeDemande/* 0 entree; 1 sortie*/) {

		H++;
		FH[porte.getIdAttribue()] = H;

		if(AJ == false){
			log.writeln("Envoit de message a toute les portes voisines...");
			try {
				Iterator<Integer> iterator = allClients.iterator();
				while(iterator.hasNext()) {
					Integer idPorte = iterator.next();
					if(idPorte != porte.getIdAttribue()){
						Message mess = new Message(H);
						log.write("from : "+porte.getIdAttribue()); log.write("to : "+idPorte); log.writeln("msg : "+ mess.toString());
						reso.sendMessage(porte.getIdAttribue(), idPorte, mess);
					}
				}

				while (!AJ) {
					log.writeln("Attente detention du jeton...");
					this.wait();
				}
				
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (RemoteException e1) {
				e1.printStackTrace();
			}
		}
		
		SC = true;
		log.writeln("Entree en Section Critique...");
		if(typeDemande == 0){//entree
			
			if(jeton.getNbrPlaceRestantes() > 0){
				jeton.decrementeNbrPlace();
				log.writeln("La Voiture est entree dans le parking!");
			}else{
				log.writeln("Plus de place dans le parking... voiture REGETER");
			}
			
			
		}else {//sortie
			jeton.incrementeNbrPlace();
			log.writeln("La voiture est sortie... NbrPlaceRestante : "+jeton.getNbrPlaceRestantes()	);
		}
		sortieSC();
	}
	
	private synchronized void sortieSC(){
		log.writeln("Sortie de section critique");
		SC = false;
		jeton.getJ()[porte.getIdAttribue()] = H;
		
		for(int i = 0; i < FH.length ; i++){
			if(FH[i] > jeton.getJ()[i] && i != porte.getIdAttribue()){
				
				try {
					AJ = false;
					log.writeln("Envoie d'un message de Type MSGJETON");
					log.write("from : "+porte.getIdAttribue()); log.write("to : "+i); log.writeln("msg : "+ jeton.toString());
					reso.sendMessage(porte.getIdAttribue(), i, jeton);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
				break;
			}
		}
		
	}

	private synchronized void receptionMSGJETON(MSGJETON msg) {
		AJ = true;
		this.jeton = msg;
		this.notify();
	}

	private void receptionREQ(int from, Message msg) {
		FH[from] =  msg.getH();
		if(AJ && SC == false){
			try {
				AJ = false;
				log.writeln("Envoie d'un message de Type MSGJETON");
				log.write("from : "+porte.getIdAttribue()); log.write("to : "+from); log.writeln("msg : "+ jeton.toString());
				reso.sendMessage(porte.getIdAttribue(), from, jeton);
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	@Override
	public synchronized void receiveMessage(int from, int to, Serializable msg) throws RemoteException {
		if(premiereFoi){
			initFH();
			premiereFoi = false;
		}
		if (msg instanceof MSGJETON) {
			log.write("Reception d'un message de type MSGJETON");
			log.write("from : "+from); log.write("to : "+to); log.writeln("msg : "+ msg.toString());
			this.receptionMSGJETON((MSGJETON) msg);
		} else if (msg instanceof Message) {
			log.write("Reception d'un message de type REQ[H]");
			log.write("from : "+from); log.write("to : "+to); log.writeln("msg : "+ msg.toString());
			this.receptionREQ(from, (Message) msg);
		}		
	}

	@Override
	public void demandeEntree() {
		if(premiereFoi){
			initFH();
			premiereFoi = false;
		}
		log.writeln("On a recu une demande d'entree...");
		acceptationDemandeSC(0);
	}
	
	@Override
	public void demandeSortie() {
		if(premiereFoi){
			initFH();
			premiereFoi = false;
		}
		log.writeln("On a recu une demande de sortie...");
		acceptationDemandeSC(1);

	}


}
